
// 获取文件域
const fileEle = document.querySelector("#file");
const uploadButton = document.querySelector("#upload");
const continueButton = document.querySelector("#continue");
uploadButton.addEventListener("click", async () => {
    console.log("点击了上传按钮")
})
continueButton.addEventListener('click', async () => {
    console.log("点击了继续上传按钮")
})


// 单个文件上传
const uploadHandler = (chunk) => {
    return new Promise(async (resolve, reject) => {
        try {
            let fd = new FormData();
            fd.append('file', chunk.file);
            fd.append('fileHash', chunk.fileHash);
            fd.append('chunkIndex', chunk.chunkIndex);
            let result = await fetch('http://localhost:3000/upload', {
                method: 'POST',
                body: fd
            }).then(res => res.json());
            chunk.uploaded = true;
            console.log('====>', result)
            resolve(result)
        } catch (err) {
            reject(err)
        }
    })
}

// -------------------------------------------------------

// 文件hash值
let fileHash = "";
// 文件名
let fileName = "";
// 使用单独常量保存预设切片大小 1MB
const chunkSize = 1024 * 1024 * 1;
// 文件切片
const createChunks = (file) => {
    // 接受一个文件对象，要把这个文件对象切片，返回一个切片数组
    const chunks = [];
    // 文件大小.slice(开始位置,结束位置)
    let start = 0;
    let index = 0;
    while (start < file.size) {
        let curChunk = file.slice(start, start + chunkSize);
        chunks.push({
            file: curChunk,
            uploaded: false,
            chunkIndex: index,
            fileHash: fileHash
        });
        index++;
        start += chunkSize;
    }
    return chunks;
}
const getHash = (file) => {
    return new Promise((resolve) => {
        const fileReader = new FileReader();
        fileReader.readAsArrayBuffer(file);
        fileReader.onload = function (e) {
            let fileMd5 = SparkMD5.ArrayBuffer.hash(e.target.result);
            resolve(fileMd5);
        }
    });
}
// 合并分片请求
const mergeRequest = (fileHash, fileName) => {
    return fetch(`http://localhost:3000/merge?fileHash=${fileHash}&fileName=${fileName}`, {
        method: "GET",
    }).then(res => res.json());
};

// 校验文件、文件分片是否存在
const verify = (fileHash, fileName) => {
    return fetch(`http://localhost:3000/verify?fileHash=${fileHash}&fileName=${fileName}`, {
        method: "GET",
    }).then(res => res.json());
};


let chunks = [];
// 上传执行函数
const uploadFile = async (file) => {


    try {
        // 设置文件名
        fileName = file.name;
        // 我的未来不是梦.mp3
        // 获取文件hash值
        fileHash = await getHash(file);
        // 校验是否已经上传该文件
        let { exitFile } = await verify(fileHash, fileName);

        if (exitFile) {
            return {
                msg: "文件已上传",
                success: true
            }
        }
        chunks = createChunks(file);
        await uploadChunksColtrol(chunks, 3)

        await mergeRequest(fileHash, fileName)

        return {
            msg: "文件上传成功"
        }
    } catch (error) {
        return {
            msg: "文件上传错误",
            success: false
        }
    }

}

// --------------------------------------------------------------


// uploadChunks & uploadChunksColtrol都是控制并发的

// 批量上传切片
// 不太好
// requestSliceArr = [[0,1,2],[3,4]]
// [0,1,2]全部处理完才会处理[3,4]
// 应该改为[0,1,2]有一个处理完就添加[3,4]中的任务，确保不让空闲
const uploadChunks = (chunks, maxRequest = 6) => {
    return new Promise((resolve, reject) => {
        if (chunks.length == 0) {
            resolve([]);
        }
        let requestSliceArr = []
        let start = 0;
        // 5
        // 0,1,2
        // 3,4
        // requestSliceArr = [[0,1,2],[3,4]]
        while (start < chunks.length) {
            requestSliceArr.push(chunks.slice(start, start + maxRequest))
            start += maxRequest;
        }
        let index = 0;
        let requestReaults = [];
        let requestErrReaults = [];
        const request = async () => {
            if (index >= requestSliceArr.length) {
                resolve(requestReaults)
                return;
            }
            let sliceChunks = requestSliceArr[index];
            Promise.all(
                sliceChunks.map(chunk => uploadHandler(chunk))
            ).then((res) => {
                requestReaults.push(...(Array.isArray(res) ? res : []))
                index++;
                request()
            }).catch((err) => {
                requestErrReaults.push(...(Array.isArray(err) ? err : []))
                reject(requestErrReaults)
            })
        }
        request()
    })
}


const uploadChunksColtrol = async (chunks, maxConcurrency) => {
    const results = [];
    const inProgress = [];

    for (const chunk of chunks) {

        const promise = uploadHandler(chunk);
        results.push(promise);

        inProgress.push(promise);
        if (inProgress.length >= maxConcurrency) {
            await Promise.race(inProgress);
        }


        promise.finally(() => {
            inProgress.splice(inProgress.indexOf(promise), 1);
        });
    }

    return Promise.all(results);
}






uploadButton.addEventListener("click", async () => {
    let file = fileEle.files[0];
    try {
        let response = await uploadFile(file)
        console.log('上传结果', response.msg)
    } catch (error) {
        console.log(error)
    }

    

})


// 断点续传
const continueUpload = async () => {
    if (chunks.length === 0 || !fileHash || !fileName) {
        return;
    }
    try {
        await uploadChunks(chunks.filter(chunk => !chunk.uploaded))
        await mergeRequest(fileHash, fileName)

        return {
            msg: '断点续传成功'
        }
    } catch (err) {
        return {
            mag: "文件上传错误",
            success: false
        }
    }
}

continueButton.addEventListener("click", async () => {

    let response = await continueUpload()

    console.log('断点续传结果', response.msg)

})




